/*
 * @Copyright: 2017 www.yyfax.com Inc. All rights reserved.
 */
package com.yyfax.pay.order.service;

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import com.yyfax.commons.json.JacksonUtil;
import com.yyfax.commons.lang.DateUtil;
import com.yyfax.framework.exception.YYException;
import com.yyfax.framework.yyfax.CryptUtil;
import com.yyfax.framework.yyfax.DesensitizationUtil;
import com.yyfax.framework.yyfax.enums.CurrencyEnum;
import com.yyfax.framework.yyfax.enums.YesNoEnum;
import com.yyfax.pay.common.constants.PayConstants;
import com.yyfax.pay.common.enums.NotifyTaskTypeEnum;
import com.yyfax.pay.common.enums.PayRspCodeEnum;
import com.yyfax.pay.common.enums.PayinStepEnum;
import com.yyfax.pay.common.enums.PaymentEnum;
import com.yyfax.pay.common.enums.QueryTaskTypeEnum;
import com.yyfax.pay.common.exception.YYPayException;
import com.yyfax.pay.config.data.service.MerchantConfigService;
import com.yyfax.pay.config.model.po.MerchantConfigPO;
import com.yyfax.pay.order.data.service.PayinOrderLogService;
import com.yyfax.pay.order.data.service.PayinOrderService;
import com.yyfax.pay.order.model.po.PayinOrderLogPO;
import com.yyfax.pay.order.model.po.PayinOrderPO;
import com.yyfax.pay.order.model.to.payin.PayinOrderContent;
import com.yyfax.pay.order.model.to.payin.PayinOrderParams;
import com.yyfax.pay.order.model.to.payin.PayinOrderVO;
import com.yyfax.pay.order.model.to.payin.PayinOrderYYContent;
import com.yyfax.pay.order.model.to.payin.PayinOrderYYParams;
import com.yyfax.pay.order.model.to.payin.PayinResultNotifyParams;
import com.yyfax.pay.order.model.to.payin.PayinResultNotifyParams.PayinNotifyParams;
import com.yyfax.pay.order.model.to.payin.PayinSnapshotVo;
import com.yyfax.pay.third.model.to.yyfax.payin.QueryPayinOrderYYContent;
import com.yyfax.pay.third.service.ThirdPayService;

/**
 * 代付业务服务
 * 
 * @author maojian
 * @date 2017-05-3 09:54:21
 * @since v1.0.0
 */
@Service
public class PayinService {
	private Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private PayinOrderService payinOrderService;

	@Autowired
	private PayinOrderLogService payinOrderLogService;

	@Autowired
	private AdapterService adapterService;

	@Autowired
	private MerchantConfigService merchantConfigService;

	@Autowired
	private PayCommonService payCommonService;
	
	@Autowired
	private PayConstants payConstants;

	/**
	 * 保存代付订单，并返回业务主键
	 * 
	 * @author maojian
	 * @date 2017-04-18 10:03:43
	 * @since v1.0.0
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public PayinSnapshotVo savePayinOrder(PayinOrderParams params) throws Throwable {
		PayinSnapshotVo payinSnapshot = new PayinSnapshotVo();
		PayinOrderPO po = new PayinOrderPO();
		BeanUtils.copyProperties(params, po);
		po.setCurrency(CurrencyEnum.RMB.getValue());
		po.setActPaymentCode(params.getPaymentCode());
		try{
			BeanUtils.copyProperties(po, payinSnapshot);
			String certNo = po.getCertNo();
			po.setCertNoCip(CryptUtil.encrypt(certNo));
			po.setCertNo(DesensitizationUtil.convertIdCard(po.getCertNo()));
			String payerCardNo = po.getPayerCardNo();
			po.setPayerCardNo(DesensitizationUtil.convertIdCard(payerCardNo));
			po.setPayerCardNoCip(CryptUtil.encrypt(payerCardNo));
			String payinOrderId = payinOrderService.saveAndReturnBusiKey(po);
			po.setPayinOrderId(payinOrderId);
			payinSnapshot.setPayinOrderId(payinOrderId);
		}catch(DuplicateKeyException e){
			logger.debug("【saveBatchPayinOrder】error:{},{},代扣订单已存在", params.getMerchantCode(), params.getOrderId(), e);
			// 幂等
			payinSnapshot = payinOrderService.querySnapshotByMerchantCodeAndOrderId(params.getMerchantCode(), params.getOrderId());
		}
		

		return payinSnapshot;
	}

	
	/**
	 * 发起代付
	 * 
	 * @author buyi
	 * @date 2017-04-18 10:38:46
	 * @since v1.0.0
	 * @param payinOrderId
	 * @throws Exception
	 */
	public PayinOrderContent payin(PayinSnapshotVo payinSnapshot) throws Exception {

		ThirdPayService thirdPayService = adapterService.adapterPayService(PaymentEnum.fromValue(payinSnapshot.getActPaymentCode()));
		PayinOrderYYParams payinOrderYYParams = new PayinOrderYYParams();
		BeanUtils.copyProperties(payinSnapshot, payinOrderYYParams);
		PayinOrderYYContent payinOrderYYContent = thirdPayService.payinOrder(payinSnapshot);

		// 保存提交日志
		savePayinOrderLog(payinSnapshot, PayinStepEnum.STEP1_SUBMIT_ORDER, payinOrderYYContent.getReqUrl(), payinOrderYYContent.getReqContent(),
				payinOrderYYContent.getRspContent(), payinOrderYYContent.getSerialNo(), payinOrderYYContent.getResultCode(),
				payinOrderYYContent.getResultMsg(), YesNoEnum.NO, null);

		// 如果订单不是失败，则生成主动查询任务
		if (!PayRspCodeEnum.PAY_CODE_PAY_FAIL.isValue(payinOrderYYContent.getResultCode())) {
			String startTime = DateUtil.datetime2Str(DateUtil.addMinute(new Date(), payConstants.getPayQueryMinuteDelay()));
			payCommonService.saveQueryTask(payinSnapshot.getMerchantCode(), payinSnapshot.getOrderId(), payinOrderYYContent.getSerialNo(),
					QueryTaskTypeEnum.PAYIN, payinSnapshot.getPayinOrderId(), payinSnapshot.getActPaymentCode(), startTime, null,
					payConstants.getQueryTaskMax(), "");
		}

		// 封装响应请求参数
		PayinOrderContent content = new PayinOrderContent();
		BeanUtils.copyProperties(payinSnapshot, content);
		content.setPaymentCode(payinSnapshot.getActPaymentCode());
		content.setResultCode(payinOrderYYContent.getResultCode());
		content.setResultMsg(payinOrderYYContent.getResultMsg());

		return content;
	}
	
	/**
	 * 保存支付日志
	 * 
	 * @author buyi
	 * @date 2017-04-17 17:31:44
	 * @since v1.0.0
	 * @param payOrderVO
	 * @param step
	 * @param reqUrl
	 * @param reqContent
	 * @param rspContent
	 * @param serialNo
	 * @param resultCode
	 * @param resultMsg
	 * @param isClose
	 * @throws Exception
	 */
	public PayinOrderLogPO savePayinOrderLog(PayinSnapshotVo payinSnapshot, PayinStepEnum step, String reqUrl, String reqContent, String rspContent,
			String serialNo, Integer resultCode, String resultMsg, YesNoEnum isClose, String remark) throws Exception {
		PayinOrderLogPO logPO = new PayinOrderLogPO();
		BeanUtils.copyProperties(payinSnapshot, logPO);
		logPO.setPaymentCode(payinSnapshot.getActPaymentCode());

		logPO.setStep(step.getValue());
		logPO.setReqUrl(reqUrl);
		logPO.setReqContent(reqContent);
		logPO.setRspContent(rspContent);
		logPO.setSerialNo(serialNo);
		logPO.setResultCode(resultCode);
		logPO.setResultMsg(resultMsg);
		logPO.setClosed(isClose.getValue());
		logPO.setRemark(remark);
		payinOrderLogService.save(logPO);

		return logPO;
	}
	
	/**
	 * 向第三方通道查询订单,并将查询结果更新到本地
	 * 
	 * @author maojian
	 * @date 2017-05-3 09:02:01
	 * @since v1.0.0
	 * @param payOrder
	 * @throws Exception
	 */
	public PayinSnapshotVo queryPayinOrder(String orderId, String merchantCode) throws Exception {
		// 1.查询订单封装VO
		PayinSnapshotVo payinOrderVO = payinOrderService.querySnapshotByMerchantCodeAndOrderId(merchantCode, orderId);
		if (payinOrderVO == null) {
			return null;
		}
		PayinOrderLogPO lastPayinOrderLog= payinOrderVO.getOrderLastResultStep() ;
		// 2.同步结果或者查询结果存在且不是进行中，则直接返回结果
		if(null != lastPayinOrderLog && (PayinStepEnum.STEP3_PAY_QUERY.isValue(lastPayinOrderLog.getStep())
				|| PayinStepEnum.STEP4_PAY_ASYN_RESULT.isValue(lastPayinOrderLog.getStep())) 
				&& !PayRspCodeEnum.PAY_CODE_DEALING.isValue(lastPayinOrderLog.getResultCode())){
			return payinOrderVO;
		}

		// 3.主动查询
		PaymentEnum payment = PaymentEnum.fromValue(payinOrderVO.getPaymentCode());
		ThirdPayService thirdPayService = adapterService.adapterPayService(payment);


		QueryPayinOrderYYContent queryContent = thirdPayService.queryPayinOrder(payinOrderVO);

		// 查询失败
		if (queryContent == null) {
			throw new YYPayException(PayRspCodeEnum.COMMON_ERR0R_NET_ERROR, "查询第三方订单失败:" + orderId);
		}

		// 处理中 或 请求超时
		if (PayRspCodeEnum.PAY_CODE_DEALING.isValue(queryContent.getResultCode())
				|| PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.isValue(queryContent.getResultCode())) {
			throw new YYPayException(PayRspCodeEnum.COMMON_ERR0R_NET_ERROR,
					"查询第三方订单结果:" + PayRspCodeEnum.fromValue(queryContent.getResultCode()));
		}


		PayinOrderLogPO logPO = savePayinOrderLog(payinOrderVO, PayinStepEnum.STEP3_PAY_QUERY, queryContent.getReqUrl(),
				queryContent.getReqContent(), queryContent.getRspContent(), queryContent.getSerialNo(),
				queryContent.getResultCode(), queryContent.getResultMsg(), YesNoEnum.NO, "主动查询");

		payinOrderVO.getOrderLogPOs().add(logPO);

		// 插入异步通知任务
		saveNotifyTask(payinOrderVO, queryContent.getResultCode(), queryContent.getResultMsg(), queryContent.getSerialNo());

		return payinOrderVO;
	}
	
	/**
	 * 保存支付日志
	 * 
	 * @author buyi
	 * @date 2017-04-17 17:31:44
	 * @since v1.0.0
	 * @param payOrderVO
	 * @param step
	 * @param reqUrl
	 * @param reqContent
	 * @param rspContent
	 * @param serialNo
	 * @param resultCode
	 * @param resultMsg
	 * @param isClose
	 * @throws Exception
	 */
	public PayinOrderLogPO savePayinOrderLog(PayinOrderPO payinOrderPO, PayinStepEnum step, String reqUrl, String reqContent, String rspContent,
			String serialNo, Integer resultCode, String resultMsg, YesNoEnum isClose, String remark) throws Exception {
		PayinOrderLogPO logPO = new PayinOrderLogPO();
		BeanUtils.copyProperties(payinOrderPO, logPO);
		logPO.setPaymentCode(payinOrderPO.getActPaymentCode());

		logPO.setStep(step.getValue());
		logPO.setReqUrl(reqUrl);
		logPO.setReqContent(reqContent);
		logPO.setRspContent(rspContent);
		logPO.setSerialNo(serialNo);
		logPO.setResultCode(resultCode);
		logPO.setResultMsg(resultMsg);
		logPO.setClosed(isClose.getValue());
		logPO.setRemark(remark);
		payinOrderLogService.save(logPO);

		return logPO;
	}
	
	/**
	 * 保存异步通知任务
	 * 
	 * @author buyi
	 * @date 2017-04-25 16:25:33
	 * @since v1.0.0
	 * @param payinOrderVO
	 * @param resultCode
	 * @param resultMsg
	 * @param serialNo
	 * @throws Exception
	 */
	private void saveNotifyTask(PayinOrderVO payinOrderVO, Integer resultCode, String resultMsg, String serialNo) throws Exception {

		// 获取商户信息
		MerchantConfigPO merchantConfigPO = merchantConfigService.queryByMerchantCode(payinOrderVO.getMerchantCode());
		if (merchantConfigPO == null) {
			throw new YYException(PayRspCodeEnum.PAY_CODE_MERCHANT_NOT_EXISTS, "商户：" + payinOrderVO.getMerchantCode() + "信息不存在");
		}

		// 封装异步通知消息
		PayinResultNotifyParams resultNotifyParams = new PayinResultNotifyParams();
		PayinNotifyParams notifyParams = new PayinNotifyParams();
		notifyParams.setResultCode(resultCode);
		notifyParams.setResultMsg(resultMsg);
		notifyParams.setMerchantCode(payinOrderVO.getMerchantCode());
		notifyParams.setOrderId(payinOrderVO.getOrderId());
		notifyParams.setUserId(payinOrderVO.getUserId());
		notifyParams.setPaymentCode(payinOrderVO.getActPaymentCode());
		notifyParams.setSerialNo(serialNo);
		notifyParams.setAmout(payinOrderVO.getAmount());

		resultNotifyParams.setSign(notifyParams.sign(merchantConfigPO.getSignKey()));
		resultNotifyParams.setParams(notifyParams);

		// 插入异步通知日志
		payCommonService.saveNotifyTask(payinOrderVO.getMerchantCode(), payinOrderVO.getOrderId(), payinOrderVO.getSerialNo(),
				NotifyTaskTypeEnum.PAYIN, payinOrderVO.getPayinOrderId(), payinOrderVO.getNotifyUrl(),
				JacksonUtil.noemptyMapper.toJson(resultNotifyParams), DateUtil.datetime2Str(new Date()), null, payConstants.getMaxNo(), "");
	}
}
